A Netflix Analysis
Installing pacman
install.packages("pacman")
Error in install.packages : Updating loaded packages
library(pacman)
Loading the packages needed
pacman::p_load(dplyr,tidyverse,tidyr, janitor, lubridate, ggplot2, leaflet, plotly, readxl)
Loading our dataset
netflix= read.csv("netflix_titles.csv")
Preview of our dataset
head(netflix, 5)
Lets check the column names
names(netflix)
[1] "show_id" "type" "title" "director" "cast" "country" "date_added"
[8] "release_year" "rating" "duration" "listed_in" "description" "X" "X.1"
[15] "X.2" "X.3" "X.4" "X.5" "X.6" "X.7" "X.8"
[22] "X.9" "X.10" "X.11" "X.12" "X.13"
Lets look at each column and its datatype
str(netflix)
'data.frame': 8809 obs. of 26 variables:
$ show_id : chr "s1" "s2" "s3" "s4" ...
$ type : chr "Movie" "TV Show" "TV Show" "TV Show" ...
$ title : chr "Dick Johnson Is Dead" "Blood & Water" "Ganglands" "Jailbirds New Orleans" ...
$ director : chr "Kirsten Johnson" "" "Julien Leclercq" "" ...
$ cast : chr "" "Ama Qamata, Khosi Ngema, Gail Mabalane, Thabang Molaba, Dillon Windvogel, Natasha Thahane, Arno Greeff, Xolile "| __truncated__ "Sami Bouajila, Tracy Gotoas, Samuel Jouy, Nabiha Akkari, Sofia Lesaffre, Salim Kechiouche, Noureddine Farihi, G"| __truncated__ "" ...
$ country : chr "United States" "South Africa" "" "" ...
$ date_added : chr "September 25, 2021" "September 24, 2021" "September 24, 2021" "September 24, 2021" ...
$ release_year: int 2020 2021 2021 2021 2021 2021 2021 1993 2021 2021 ...
$ rating : chr "PG-13" "TV-MA" "TV-MA" "TV-MA" ...
$ duration : chr "90 min" "2 Seasons" "1 Season" "1 Season" ...
$ listed_in : chr "Documentaries" "International TV Shows, TV Dramas, TV Mysteries" "Crime TV Shows, International TV Shows, TV Action & Adventure" "Docuseries, Reality TV" ...
$ description : chr "As her father nears the end of his life, filmmaker Kirsten Johnson stages his death in inventive and comical wa"| __truncated__ "After crossing paths at a party, a Cape Town teen sets out to prove whether a private-school swimming star is h"| __truncated__ "To protect his family from a powerful drug lord, skilled thief Mehdi and his expert team of robbers are pulled "| __truncated__ "Feuds, flirtations and toilet talk go down among the incarcerated women at the Orleans Justice Center in New Or"| __truncated__ ...
$ X : logi NA NA NA NA NA NA ...
$ X.1 : logi NA NA NA NA NA NA ...
$ X.2 : logi NA NA NA NA NA NA ...
$ X.3 : logi NA NA NA NA NA NA ...
$ X.4 : logi NA NA NA NA NA NA ...
$ X.5 : logi NA NA NA NA NA NA ...
$ X.6 : logi NA NA NA NA NA NA ...
$ X.7 : logi NA NA NA NA NA NA ...
$ X.8 : logi NA NA NA NA NA NA ...
$ X.9 : logi NA NA NA NA NA NA ...
$ X.10 : logi NA NA NA NA NA NA ...
$ X.11 : logi NA NA NA NA NA NA ...
$ X.12 : logi NA NA NA NA NA NA ...
$ X.13 : logi NA NA NA NA NA NA ...
Lets convert the type to factor
netflix$type= as.factor(netflix$type)
Investigating Null values
NAs=colSums(is.na(netflix))
names(netflix)[NAs>0]
[1] "X" "X.1" "X.2" "X.3" "X.4" "X.5" "X.6" "X.7" "X.8" "X.9" "X.10" "X.11" "X.12" "X.13"
Dim
dim(netflix)
[1] 8809 26
Total rows with NAs in each column
colSums(is.na(netflix))
show_id type title director cast country date_added release_year
0 0 0 0 0 0 0 0
rating duration listed_in description X X.1 X.2 X.3
0 0 0 0 8809 8809 8809 8809
X.4 X.5 X.6 X.7 X.8 X.9 X.10 X.11
8809 8809 8809 8809 8809 8809 8809 8809
X.12 X.13
8809 8809
Since the columns with NAs dont have much meaning to the dataset we
can remove them
netflix= netflix %>%
select(show_id,type,title,director,cast,country,date_added,release_year,rating,duration,listed_in,description)
Descriptive Statistics:
Summarize the distribution of the types of shows (Movies vs. TV
Shows).
#Distribution of movie type
netflix_type= netflix %>%
select(type) %>%
group_by(type) %>%
summarise(totalcount= n())
# Calculate percentage labels
percentages <- round(netflix_type$totalcount / sum(netflix_type$totalcount) * 100, 1)
labels <- paste(netflix_type$type, percentages, "%", sep = " ")
# Create the pie chart using plotly
fig <- plot_ly(netflix_type, labels = ~type, values = ~totalcount, type = 'pie',
textinfo = 'label+percent',
insidetextorientation = 'radial',
marker = list(colors = c('red', 'green')))
# Customize the layout
fig <- fig %>% layout(title = 'Distribution of TV Shows and Movies')
# Display the plot
fig
Calculate the number of shows released per year.
netflix_releaseyear= netflix %>%
select(release_year, type) %>%
group_by(release_year, type) %>%
summarise(total_moveis_or_shows= n(), .groups = "drop")
netflix_release_year= netflix %>%
select(release_year, type) %>%
group_by(release_year) %>%
summarise(total_shows= n()) %>%
arrange(desc(total_shows)) %>%
head(10)
fig4=plot_ly(netflix_release_year, x= ~release_year, y= ~total_shows,type= 'bar',
text= ~total_shows,
textposition = "auto",
marker=list(color="green")) %>%
layout(title= "Top 10 years with the highest number of shows produced",
xaxis= list(title="Years"),
yaxis= list(title="Total Shows"))
fig4
Analyze the distribution of ratings (e.g., TV-MA, PG-13, etc.).
netflix_ratings= netflix %>%
select(rating) %>%
group_by(rating) %>%
summarize(total_rating=n())
#the plot
fig3 =plot_ly(netflix_ratings, x= ~rating, y= ~total_rating, type='bar', marker=list(color= "red"))%>%
layout(title="Distribution of the ratings",
xaxis= list(title="Ratings"),
yaxis= list(title="Total ratings"))
fig3
Trend Analysis:
Analyze the popularity of different genres over the years.
netflix_genre= netflix %>%
select(release_year, listed_in) %>%
group_by(release_year, listed_in) %>%
summarise(total_shows= n(), .groups = "drop")
Genre Analysis:
Determine the most common genres listed.
netflix_genre_common= netflix %>%
select(listed_in) %>%
group_by(listed_in) %>%
summarise(total_count= n()) %>%
arrange(desc(total_count)) %>%
head(10)
fig5 = plot_ly(netflix_genre_common, y= ~listed_in, x= ~total_count, type= 'bar',
text= ~total_count,
textposition= "auto",
marker= list(color="orange")) %>%
layout(
title= "Top 10 Most common genres Listed",
yaxis = list(title="Genres"),
xaxis = list(title="Total Count")
)
fig5
Analyze the correlation between genres and ratings.
netflix_corr_genre= netflix %>%
select(listed_in, rating) %>%
group_by(listed_in, rating) %>%
summarise(total_count = n(), .groups = "drop")
print(netflix_corr_genre)
Country Analysis:
Analyze the Countries With the most movies produced
netflix_country= netflix %>%
select(country) %>%
mutate(country= paste0(country, ","))
netflix_country = netflix_country %>%
separate(col= country, into= c("Country", "Rest"), sep=",")
netflix_country_grouped= netflix_country %>%
group_by(Country) %>%
summarise(Total_Movies= n()) %>%
arrange(desc(Total_Movies))
# Convert empty strings to NA in the 'Country' column
netflix_country_grouped_clean <- netflix_country_grouped %>%
mutate(Country = na_if(Country, ""))
# Remove rows with NA values
netflix_country_grouped_clean <- na.omit(netflix_country_grouped_clean)
netflix_country_grouped_plot= head(netflix_country_grouped, 10)
fig8 = plot_ly(netflix_country_grouped_plot, x= ~ Country, y= ~Total_Movies, type= 'bar',
text= ~Total_Movies,
markers=list(color="yellow")) %>%
layout(
title = "Top 10 Countries with the Highest No of movies Produced",
xaxis = list(title= "Countries", tickangle= -45),
yaxis = list(title= "Total Movies Produced"))
fig8
NA
Map
# # Get world map data for country coordinates
# world_map <- map_data("world")
#
# # Prepare the data by merging with coordinates
# country_coords <- world_map %>%
# group_by(region) %>%
# summarize(
# lat = mean(lat),
# lng = mean(long)
# ) %>%
# rename(Country = region)
#
# # Merge country data with coordinates
# map_data <- netflix_country_grouped_clean %>%
# left_join(country_coords, by = "Country")
#
# # Filter out rows with missing or invalid coordinates
# map_data_filtered <- map_data %>%
# filter(!is.na(lat) & !is.na(lng))
#
# # Create an interactive map with markers
# m <- leaflet(map_data_filtered) %>%
# addTiles() %>%
# addMarkers(
# clusterOptions = markerClusterOptions(),
# ~lng, ~lat,
# popup = ~paste("<strong>Country:</strong>", Country, "<br>",
# "<strong>Value:</strong>", Total_Movies)
# )
#
# # Display the map
# m
Analyze the diversity of content by country.
United States
netflix_diversity= netflix %>%
select(country, listed_in) %>%
group_by(country, listed_in) %>%
summarise(total_count=n(), .groups = "drop") %>%
arrange(desc(total_count))
print(netflix_diversity)
Duration Analysis:
Compare the average duration of movies vs. TV shows.
netflix$duration= as.character(netflix$duration)
netflix_duration <- netflix %>%
select(type, duration) %>%
separate(col = duration, into = c("duration", "units"), sep = " ")
# Convert duration back to integer
netflix_duration$duration <- as.integer(netflix_duration$duration)
netflix_duration_compison_type= netflix_duration %>%
group_by(type) %>%
summarise(Average_Duration= floor(mean(duration, na.rm = TRUE)))
print(netflix_duration_compison_type)
Analyze the distribution of the number of seasons for TV shows.
netflix_tvshows_distribution= netflix %>%
select(type, duration) %>%
filter(type == "TV Show") %>%
group_by(duration) %>%
summarise(Frequency_totals= n()) %>%
arrange(desc(Frequency_totals))
netflix_tvshows_distribution$duration= factor(netflix_tvshows_distribution$duration, levels= unique(netflix_tvshows_distribution$duration))
# Create a bar chart
fig6 = plot_ly(netflix_tvshows_distribution, x = ~duration, y= ~Frequency_totals, type = 'bar',
marker=list(color="green")) %>%
layout(
title= "Distribution of Seasons in the TV Shows",
xaxis= list(title ="Duration", tickangle=-45),
yaxis= list(title= "Frequency")
)
# Display the plot
fig6
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIEEgTmV0ZmxpeCBBbmFseXNpcw0KIyMjIyBJbnN0YWxsaW5nIHBhY21hbg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRX0NCmluc3RhbGwucGFja2FnZXMoInBhY21hbiIpDQpsaWJyYXJ5KHBhY21hbikNCmBgYA0KIyMjIyBMb2FkaW5nIHRoZSBwYWNrYWdlcyBuZWVkZWQNCmBgYHtyfQ0KcGFjbWFuOjpwX2xvYWQoZHBseXIsdGlkeXZlcnNlLHRpZHlyLCAgamFuaXRvciwgbHVicmlkYXRlLCBnZ3Bsb3QyLCBsZWFmbGV0LCBwbG90bHksIHJlYWR4bCkNCmBgYA0KIyMjIyBMb2FkaW5nIG91ciBkYXRhc2V0DQpgYGB7cn0NCm5ldGZsaXg9IHJlYWQuY3N2KCJuZXRmbGl4X3RpdGxlcy5jc3YiKQ0KYGBgDQojIyMjIFByZXZpZXcgb2Ygb3VyIGRhdGFzZXQNCmBgYHtyfQ0KaGVhZChuZXRmbGl4LCA1KQ0KYGBgDQojIyMjIExldHMgY2hlY2sgdGhlIGNvbHVtbiBuYW1lcw0KYGBge3J9DQpuYW1lcyhuZXRmbGl4KQ0KYGBgDQojIyMjIExldHMgbG9vayBhdCBlYWNoIGNvbHVtbiBhbmQgaXRzIGRhdGF0eXBlDQpgYGB7cn0NCnN0cihuZXRmbGl4KQ0KYGBgDQojIyMjIExldHMgY29udmVydCB0aGUgdHlwZSB0byBmYWN0b3INCmBgYHtyfQ0KbmV0ZmxpeCR0eXBlPSBhcy5mYWN0b3IobmV0ZmxpeCR0eXBlKQ0KYGBgDQoNCiMjIyMgSW52ZXN0aWdhdGluZyBOdWxsIHZhbHVlcw0KYGBge3J9DQpOQXM9Y29sU3Vtcyhpcy5uYShuZXRmbGl4KSkNCm5hbWVzKG5ldGZsaXgpW05Bcz4wXQ0KYGBgDQojIyMjIERpbQ0KYGBge3J9DQpkaW0obmV0ZmxpeCkNCmBgYA0KIyMjIyBUb3RhbCByb3dzIHdpdGggTkFzIGluIGVhY2ggY29sdW1uDQpgYGB7cn0NCmNvbFN1bXMoaXMubmEobmV0ZmxpeCkpDQpgYGANCg0KIyMjIyBTaW5jZSB0aGUgY29sdW1ucyB3aXRoIE5BcyBkb250IGhhdmUgbXVjaCBtZWFuaW5nIHRvIHRoZSBkYXRhc2V0IHdlIGNhbiByZW1vdmUgdGhlbQ0KYGBge3J9DQpuZXRmbGl4PSBuZXRmbGl4ICU+JSANCiAgc2VsZWN0KHNob3dfaWQsdHlwZSx0aXRsZSxkaXJlY3RvcixjYXN0LGNvdW50cnksZGF0ZV9hZGRlZCxyZWxlYXNlX3llYXIscmF0aW5nLGR1cmF0aW9uLGxpc3RlZF9pbixkZXNjcmlwdGlvbikNCmBgYA0KDQojIyBEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzOg0KIyMjIFN1bW1hcml6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB0eXBlcyBvZiBzaG93cyAoTW92aWVzIHZzLiBUViBTaG93cykuDQpgYGB7cn0NCiNEaXN0cmlidXRpb24gb2YgbW92aWUgdHlwZQ0KbmV0ZmxpeF90eXBlPSBuZXRmbGl4ICU+JSANCiAgc2VsZWN0KHR5cGUpICU+JSANCiAgZ3JvdXBfYnkodHlwZSkgJT4lIA0KICBzdW1tYXJpc2UodG90YWxjb3VudD0gbigpKQ0KDQojIENhbGN1bGF0ZSBwZXJjZW50YWdlIGxhYmVscw0KcGVyY2VudGFnZXMgPC0gcm91bmQobmV0ZmxpeF90eXBlJHRvdGFsY291bnQgLyBzdW0obmV0ZmxpeF90eXBlJHRvdGFsY291bnQpICogMTAwLCAxKQ0KbGFiZWxzIDwtIHBhc3RlKG5ldGZsaXhfdHlwZSR0eXBlLCBwZXJjZW50YWdlcywgIiUiLCBzZXAgPSAiICIpDQoNCiMgQ3JlYXRlIHRoZSBwaWUgY2hhcnQgdXNpbmcgcGxvdGx5DQpmaWcgPC0gcGxvdF9seShuZXRmbGl4X3R5cGUsIGxhYmVscyA9IH50eXBlLCB2YWx1ZXMgPSB+dG90YWxjb3VudCwgdHlwZSA9ICdwaWUnLCANCiAgICAgICAgICAgICAgIHRleHRpbmZvID0gJ2xhYmVsK3BlcmNlbnQnLA0KICAgICAgICAgICAgICAgaW5zaWRldGV4dG9yaWVudGF0aW9uID0gJ3JhZGlhbCcsDQogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9ycyA9IGMoJ3JlZCcsICdncmVlbicpKSkNCg0KIyBDdXN0b21pemUgdGhlIGxheW91dA0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBUViBTaG93cyBhbmQgTW92aWVzJykNCg0KIyBEaXNwbGF5IHRoZSBwbG90DQpmaWcNCmBgYA0KIyMjIENhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIHNob3dzIHJlbGVhc2VkIHBlciB5ZWFyLg0KYGBge3J9DQpuZXRmbGl4X3JlbGVhc2V5ZWFyPSBuZXRmbGl4ICU+JSANCiAgc2VsZWN0KHJlbGVhc2VfeWVhciwgdHlwZSkgJT4lIA0KICBncm91cF9ieShyZWxlYXNlX3llYXIsIHR5cGUpICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsX21vdmVpc19vcl9zaG93cz0gbigpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQpuZXRmbGl4X3JlbGVhc2VfeWVhcj0gbmV0ZmxpeCAlPiUgDQogIHNlbGVjdChyZWxlYXNlX3llYXIsIHR5cGUpICU+JSANCiAgZ3JvdXBfYnkocmVsZWFzZV95ZWFyKSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbF9zaG93cz0gbigpKSAlPiUgDQogIGFycmFuZ2UoZGVzYyh0b3RhbF9zaG93cykpICU+JSANCiAgaGVhZCgxMCkNCg0KZmlnND1wbG90X2x5KG5ldGZsaXhfcmVsZWFzZV95ZWFyLCB4PSB+cmVsZWFzZV95ZWFyLCB5PSB+dG90YWxfc2hvd3MsdHlwZT0gJ2JhcicsDQogICAgICAgICAgICAgdGV4dD0gfnRvdGFsX3Nob3dzLA0KICAgICAgICAgICAgIHRleHRwb3NpdGlvbiA9ICJhdXRvIiwNCiAgICAgICAgICAgICBtYXJrZXI9bGlzdChjb2xvcj0iZ3JlZW4iKSkgJT4lIA0KICBsYXlvdXQodGl0bGU9ICJUb3AgMTAgeWVhcnMgd2l0aCB0aGUgaGlnaGVzdCBudW1iZXIgb2Ygc2hvd3MgcHJvZHVjZWQiLA0KICAgICAgICAgeGF4aXM9IGxpc3QodGl0bGU9IlllYXJzIiksDQogICAgICAgICB5YXhpcz0gbGlzdCh0aXRsZT0iVG90YWwgU2hvd3MiKSkNCmZpZzQNCmBgYA0KDQojIyMgQW5hbHl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHJhdGluZ3MgKGUuZy4sIFRWLU1BLCBQRy0xMywgZXRjLikuDQpgYGB7cn0NCm5ldGZsaXhfcmF0aW5ncz0gbmV0ZmxpeCAlPiUNCiAgc2VsZWN0KHJhdGluZykgJT4lIA0KICBncm91cF9ieShyYXRpbmcpICU+JSANCiAgc3VtbWFyaXplKHRvdGFsX3JhdGluZz1uKCkpDQoNCiN0aGUgcGxvdA0KZmlnMyA9cGxvdF9seShuZXRmbGl4X3JhdGluZ3MsIHg9IH5yYXRpbmcsIHk9IH50b3RhbF9yYXRpbmcsIHR5cGU9J2JhcicsIG1hcmtlcj1saXN0KGNvbG9yPSAicmVkIikpJT4lIA0KICBsYXlvdXQodGl0bGU9IkRpc3RyaWJ1dGlvbiBvZiB0aGUgcmF0aW5ncyIsDQogICAgICAgICB4YXhpcz0gbGlzdCh0aXRsZT0iUmF0aW5ncyIpLA0KICAgICAgICAgeWF4aXM9IGxpc3QodGl0bGU9IlRvdGFsIHJhdGluZ3MiKSkNCg0KZmlnMw0KYGBgDQojIyBUcmVuZCBBbmFseXNpczoNCiMjIyBFeHBsb3JlIHRoZSB0cmVuZHMgaW4gdGhlIG51bWJlciBvZiBzaG93cyBhZGRlZCB0byB0aGUgcGxhdGZvcm0gb3ZlciB0aW1lLg0KYGBge3J9DQpuZXRmbGl4X3JlbGVhc2VfeWVhcl9ncm91cD0gbmV0ZmxpeCAlPiUgDQogIHNlbGVjdChyZWxlYXNlX3llYXIsIHR5cGUpICU+JSANCiAgZ3JvdXBfYnkocmVsZWFzZV95ZWFyKSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbF9zaG93cz0gbigpKQ0KDQpuZXRmbGl4X3JlbGVhc2VfeWVhcl9ncm91cGVkPSBuZXRmbGl4X3JlbGVhc2VfeWVhcl9ncm91cCAlPiUgDQogIG11dGF0ZShyZWxlYXNlX3llYXJfZ3JvdXBlZD0gY2FzZV93aGVuKA0KICAgIHJlbGVhc2VfeWVhciA8IDE5MzAgfiAiMTkyMC0xOTMwIiwNCiAgICByZWxlYXNlX3llYXIgPCAxOTQwIH4gIjE5MzAtMTk0MCIsDQogICAgcmVsZWFzZV95ZWFyIDwgMTk1MCB+ICIxOTQwLTE5NTAiLA0KICAgIHJlbGVhc2VfeWVhciA8IDE5NjAgfiAiMTk1MC0xOTYwIiwNCiAgICByZWxlYXNlX3llYXIgPCAxOTcwIH4gIjE5NTAtMTk3MCIsDQogICAgcmVsZWFzZV95ZWFyIDwgMTk4MCB+ICIxOTcwLTE5ODAiLA0KICAgIHJlbGVhc2VfeWVhciA8IDE5OTAgfiAiMTk4MC0xOTkwIiwNCiAgICByZWxlYXNlX3llYXIgPCAyMDAwIH4gIjE5OTAtMjAwMCIsDQogICAgcmVsZWFzZV95ZWFyIDwgMjAxMCB+ICIyMDAwLTIwMTAiLA0KICAgIHJlbGVhc2VfeWVhciA8IDIwMjAgfiAiMjAxMC0yMDIwIiwNCiAgICByZWxlYXNlX3llYXIgPj0gMjAyMCB+ICIyMDIwIikpICU+JSANCiAgZ3JvdXBfYnkocmVsZWFzZV95ZWFyX2dyb3VwZWQpICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsX3Nob3dzID0gc3VtKHRvdGFsX3Nob3dzKSkNCg0KI3VzaW5nIHBsb3RseSB0byBkcmF3IHRoZSBsaW5lIENoYXJ0DQpmaWcxIDwtIHBsb3RfbHkobmV0ZmxpeF9yZWxlYXNlX3llYXJfZ3JvdXBlZCwgeCA9IH5yZWxlYXNlX3llYXJfZ3JvdXBlZCwgeSA9IH50b3RhbF9zaG93cywgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycpICU+JQ0KICBsYXlvdXQodGl0bGUgPSAnTmV0ZmxpeCBTaG93cyBieSBSZWxlYXNlIFllYXInLA0KICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ1JlbGVhc2UgWWVhcicsIHRpY2thbmdsZT0gLTQ1KSwNCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdUb3RhbCBTaG93cycpKQ0KDQojcHJpbnRpbmcgdGhlIHBsb3QNCmZpZzENCmBgYA0KDQojIyMgQW5hbHl6ZSB0aGUgcG9wdWxhcml0eSBvZiBkaWZmZXJlbnQgZ2VucmVzIG92ZXIgdGhlIHllYXJzLg0KYGBge3J9DQpuZXRmbGl4X2dlbnJlPSBuZXRmbGl4ICU+JSANCiAgc2VsZWN0KHJlbGVhc2VfeWVhciwgbGlzdGVkX2luKSAlPiUgDQogIGdyb3VwX2J5KHJlbGVhc2VfeWVhciwgbGlzdGVkX2luKSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbF9zaG93cz0gbigpLCAuZ3JvdXBzID0gImRyb3AiKQ0KYGBgDQojIyBHZW5yZSBBbmFseXNpczoNCiMjIyBEZXRlcm1pbmUgdGhlIG1vc3QgY29tbW9uIGdlbnJlcyBsaXN0ZWQuDQpgYGB7cn0NCm5ldGZsaXhfZ2VucmVfY29tbW9uPSBuZXRmbGl4ICU+JSANCiAgc2VsZWN0KGxpc3RlZF9pbikgJT4lIA0KICBncm91cF9ieShsaXN0ZWRfaW4pICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsX2NvdW50PSBuKCkpICU+JSANCiAgYXJyYW5nZShkZXNjKHRvdGFsX2NvdW50KSkgJT4lIA0KICBoZWFkKDEwKQ0KDQpmaWc1ID0gcGxvdF9seShuZXRmbGl4X2dlbnJlX2NvbW1vbiwgeT0gfmxpc3RlZF9pbiwgeD0gfnRvdGFsX2NvdW50LCB0eXBlPSAnYmFyJywNCiAgICAgICAgICAgICAgIHRleHQ9IH50b3RhbF9jb3VudCwNCiAgICAgICAgICAgICAgIHRleHRwb3NpdGlvbj0gImF1dG8iLA0KICAgICAgICAgICAgICAgbWFya2VyPSBsaXN0KGNvbG9yPSJvcmFuZ2UiKSkgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGU9ICJUb3AgMTAgTW9zdCBjb21tb24gZ2VucmVzIExpc3RlZCIsDQogICAgeWF4aXMgPSBsaXN0KHRpdGxlPSJHZW5yZXMiKSwNCiAgICB4YXhpcyA9IGxpc3QodGl0bGU9IlRvdGFsIENvdW50IikNCiAgKQ0KDQpmaWc1DQpgYGANCg0KIyMjIEFuYWx5emUgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gZ2VucmVzIGFuZCByYXRpbmdzLg0KYGBge3J9DQpuZXRmbGl4X2NvcnJfZ2VucmU9IG5ldGZsaXggJT4lIA0KICBzZWxlY3QobGlzdGVkX2luLCByYXRpbmcpICU+JSANCiAgZ3JvdXBfYnkobGlzdGVkX2luLCByYXRpbmcpICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsX2NvdW50ID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKQ0KIHByaW50KG5ldGZsaXhfY29ycl9nZW5yZSkNCmBgYA0KDQojIyBDb3VudHJ5IEFuYWx5c2lzOg0KIyMjIEFuYWx5emUgdGhlIENvdW50cmllcyBXaXRoIHRoZSBtb3N0IG1vdmllcyBwcm9kdWNlZA0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpuZXRmbGl4X2NvdW50cnk9IG5ldGZsaXggJT4lIA0KICBzZWxlY3QoY291bnRyeSkgJT4lIA0KICBtdXRhdGUoY291bnRyeT0gcGFzdGUwKGNvdW50cnksICIsIikpDQoNCm5ldGZsaXhfY291bnRyeSA9IG5ldGZsaXhfY291bnRyeSAlPiUgDQogIHNlcGFyYXRlKGNvbD0gY291bnRyeSwgaW50bz0gYygiQ291bnRyeSIsICJSZXN0IiksIHNlcD0iLCIpDQoNCm5ldGZsaXhfY291bnRyeV9ncm91cGVkPSBuZXRmbGl4X2NvdW50cnkgJT4lIA0KICBncm91cF9ieShDb3VudHJ5KSAlPiUgDQogIHN1bW1hcmlzZShUb3RhbF9Nb3ZpZXM9IG4oKSkgJT4lIA0KICBhcnJhbmdlKGRlc2MoVG90YWxfTW92aWVzKSkNCiMgQ29udmVydCBlbXB0eSBzdHJpbmdzIHRvIE5BIGluIHRoZSAnQ291bnRyeScgY29sdW1uDQpuZXRmbGl4X2NvdW50cnlfZ3JvdXBlZF9jbGVhbiA8LSBuZXRmbGl4X2NvdW50cnlfZ3JvdXBlZCAlPiUNCiAgbXV0YXRlKENvdW50cnkgPSBuYV9pZihDb3VudHJ5LCAiIikpDQoNCiMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSB2YWx1ZXMNCm5ldGZsaXhfY291bnRyeV9ncm91cGVkX2NsZWFuIDwtIG5hLm9taXQobmV0ZmxpeF9jb3VudHJ5X2dyb3VwZWRfY2xlYW4pDQpuZXRmbGl4X2NvdW50cnlfZ3JvdXBlZF9wbG90PSBoZWFkKG5ldGZsaXhfY291bnRyeV9ncm91cGVkLCAxMCkNCg0KZmlnOCA9IHBsb3RfbHkobmV0ZmxpeF9jb3VudHJ5X2dyb3VwZWRfcGxvdCwgeD0gfiBDb3VudHJ5LCB5PSB+VG90YWxfTW92aWVzLCB0eXBlPSAnYmFyJywNCiAgICAgICAgICAgICAgIHRleHQ9IH5Ub3RhbF9Nb3ZpZXMsDQogICAgICAgICAgICAgICBtYXJrZXJzPWxpc3QoY29sb3I9InllbGxvdyIpKSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJUb3AgMTAgQ291bnRyaWVzIHdpdGggdGhlIEhpZ2hlc3QgTm8gb2YgbW92aWVzIFByb2R1Y2VkIiwNCiAgICB4YXhpcyA9IGxpc3QodGl0bGU9ICJDb3VudHJpZXMiLCB0aWNrYW5nbGU9IC00NSksDQogICAgeWF4aXMgPSBsaXN0KHRpdGxlPSAiVG90YWwgTW92aWVzIFByb2R1Y2VkIikpDQogZmlnOCANCg0KYGBgDQojIE1hcA0KYGBge3J9DQojICMgR2V0IHdvcmxkIG1hcCBkYXRhIGZvciBjb3VudHJ5IGNvb3JkaW5hdGVzDQojIHdvcmxkX21hcCA8LSBtYXBfZGF0YSgid29ybGQiKQ0KIyANCiMgIyBQcmVwYXJlIHRoZSBkYXRhIGJ5IG1lcmdpbmcgd2l0aCBjb29yZGluYXRlcw0KIyBjb3VudHJ5X2Nvb3JkcyA8LSB3b3JsZF9tYXAgJT4lDQojICAgZ3JvdXBfYnkocmVnaW9uKSAlPiUNCiMgICBzdW1tYXJpemUoDQojICAgICBsYXQgPSBtZWFuKGxhdCksDQojICAgICBsbmcgPSBtZWFuKGxvbmcpDQojICAgKSAlPiUNCiMgICByZW5hbWUoQ291bnRyeSA9IHJlZ2lvbikNCiMgDQojICMgTWVyZ2UgY291bnRyeSBkYXRhIHdpdGggY29vcmRpbmF0ZXMNCiMgbWFwX2RhdGEgPC0gbmV0ZmxpeF9jb3VudHJ5X2dyb3VwZWRfY2xlYW4gJT4lDQojICAgbGVmdF9qb2luKGNvdW50cnlfY29vcmRzLCBieSA9ICJDb3VudHJ5IikNCiMgDQojICMgRmlsdGVyIG91dCByb3dzIHdpdGggbWlzc2luZyBvciBpbnZhbGlkIGNvb3JkaW5hdGVzDQojIG1hcF9kYXRhX2ZpbHRlcmVkIDwtIG1hcF9kYXRhICU+JQ0KIyAgIGZpbHRlcighaXMubmEobGF0KSAmICFpcy5uYShsbmcpKQ0KIyANCiMgIyBDcmVhdGUgYW4gaW50ZXJhY3RpdmUgbWFwIHdpdGggbWFya2Vycw0KIyBtIDwtIGxlYWZsZXQobWFwX2RhdGFfZmlsdGVyZWQpICU+JQ0KIyAgIGFkZFRpbGVzKCkgJT4lDQojICAgYWRkTWFya2VycygNCiMgICAgIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKSwNCiMgICAgIH5sbmcsIH5sYXQsDQojICAgICBwb3B1cCA9IH5wYXN0ZSgiPHN0cm9uZz5Db3VudHJ5Ojwvc3Ryb25nPiIsIENvdW50cnksICI8YnI+IiwNCiMgICAgICAgICAgICAgICAgICAgICI8c3Ryb25nPlZhbHVlOjwvc3Ryb25nPiIsIFRvdGFsX01vdmllcykNCiMgICApDQojIA0KIyAjIERpc3BsYXkgdGhlIG1hcA0KIyBtDQoNCmBgYA0KIyMjIEFuYWx5emUgdGhlIGRpdmVyc2l0eSBvZiBjb250ZW50IGJ5IGNvdW50cnkuDQojIyMjIFVuaXRlZCBTdGF0ZXMNCmBgYHtyfQ0KbmV0ZmxpeF9kaXZlcnNpdHk9IG5ldGZsaXggJT4lIA0KICBzZWxlY3QoY291bnRyeSwgbGlzdGVkX2luKSAlPiUgDQogIGdyb3VwX2J5KGNvdW50cnksIGxpc3RlZF9pbikgJT4lIA0KICBzdW1tYXJpc2UodG90YWxfY291bnQ9bigpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUgDQogIGFycmFuZ2UoZGVzYyh0b3RhbF9jb3VudCkpDQpwcmludChuZXRmbGl4X2RpdmVyc2l0eSkNCmBgYA0KIyMgRHVyYXRpb24gQW5hbHlzaXM6DQojIyMgQ29tcGFyZSB0aGUgYXZlcmFnZSBkdXJhdGlvbiBvZiBtb3ZpZXMgdnMuIFRWIHNob3dzLg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpuZXRmbGl4JGR1cmF0aW9uPSBhcy5jaGFyYWN0ZXIobmV0ZmxpeCRkdXJhdGlvbikNCg0KbmV0ZmxpeF9kdXJhdGlvbiA8LSBuZXRmbGl4ICU+JQ0KICBzZWxlY3QodHlwZSwgZHVyYXRpb24pICU+JQ0KICBzZXBhcmF0ZShjb2wgPSBkdXJhdGlvbiwgaW50byA9IGMoImR1cmF0aW9uIiwgInVuaXRzIiksIHNlcCA9ICIgIikNCg0KIyBDb252ZXJ0IGR1cmF0aW9uIGJhY2sgdG8gaW50ZWdlcg0KbmV0ZmxpeF9kdXJhdGlvbiRkdXJhdGlvbiA8LSBhcy5pbnRlZ2VyKG5ldGZsaXhfZHVyYXRpb24kZHVyYXRpb24pDQoNCm5ldGZsaXhfZHVyYXRpb25fY29tcGlzb25fdHlwZT0gbmV0ZmxpeF9kdXJhdGlvbiAlPiUgDQogIGdyb3VwX2J5KHR5cGUpICU+JQ0KICBzdW1tYXJpc2UoQXZlcmFnZV9EdXJhdGlvbj0gZmxvb3IobWVhbihkdXJhdGlvbiwgbmEucm0gPSBUUlVFKSkpDQoNCnByaW50KG5ldGZsaXhfZHVyYXRpb25fY29tcGlzb25fdHlwZSkNCmBgYA0KIyMjIEFuYWx5emUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbnVtYmVyIG9mIHNlYXNvbnMgZm9yIFRWIHNob3dzLg0KYGBge3J9DQpuZXRmbGl4X3R2c2hvd3NfZGlzdHJpYnV0aW9uPSBuZXRmbGl4ICU+JSANCiAgc2VsZWN0KHR5cGUsIGR1cmF0aW9uKSAlPiUgDQogIGZpbHRlcih0eXBlID09ICJUViBTaG93IikgJT4lIA0KICBncm91cF9ieShkdXJhdGlvbikgJT4lIA0KICBzdW1tYXJpc2UoRnJlcXVlbmN5X3RvdGFscz0gbigpKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhGcmVxdWVuY3lfdG90YWxzKSkNCg0KbmV0ZmxpeF90dnNob3dzX2Rpc3RyaWJ1dGlvbiRkdXJhdGlvbj0gZmFjdG9yKG5ldGZsaXhfdHZzaG93c19kaXN0cmlidXRpb24kZHVyYXRpb24sIGxldmVscz0gdW5pcXVlKG5ldGZsaXhfdHZzaG93c19kaXN0cmlidXRpb24kZHVyYXRpb24pKQ0KIyBDcmVhdGUgYSBiYXIgY2hhcnQNCmZpZzYgPSBwbG90X2x5KG5ldGZsaXhfdHZzaG93c19kaXN0cmlidXRpb24sIHggPSB+ZHVyYXRpb24sIHk9IH5GcmVxdWVuY3lfdG90YWxzLCB0eXBlID0gJ2JhcicsDQogICAgICAgICAgICBtYXJrZXI9bGlzdChjb2xvcj0iZ3JlZW4iKSkgJT4lDQogIGxheW91dCgNCiAgICB0aXRsZT0gIkRpc3RyaWJ1dGlvbiBvZiBTZWFzb25zIGluIHRoZSBUViBTaG93cyIsDQogICAgeGF4aXM9IGxpc3QodGl0bGUgPSJEdXJhdGlvbiIsIHRpY2thbmdsZT0tNDUpLA0KICAgIHlheGlzPSBsaXN0KHRpdGxlPSAiRnJlcXVlbmN5IikNCiAgKQ0KDQojIERpc3BsYXkgdGhlIHBsb3QNCmZpZzYNCmBgYA0K